<div id="data-preview" ref:hot />

<script>
    /* global dw,chart */
    import HOT from 'Handsontable';
    import getCellRenderer from './getCellRenderer';
    // import cellHeaderClick from './cellHeaderClick';
    import clone from '@datawrapper/shared/clone';

    let app = null;
    let searchTimer = null;

    export default {
        data() {
            return {
                hot: null,
                data: '',
                readonly: false,
                skipRows: 0,
                firstRowIsHeader: true,
                fixedColumnsLeft: 0,
                searchIndex: 0,
                sortBy: '-',
                transpose: false,
                activeColumn: null,
                search: '',
                searchResults: []
            };
        },
        computed: {
            currentResult({ searchResults, searchIndex }) {
                if (!searchResults || !searchResults.length) return null;
                const l = searchResults.length;
                if (searchIndex < 0 || searchIndex >= l) {
                    while (searchIndex < 0) searchIndex += l;
                    if (searchIndex > l) searchIndex %= l;
                }
                return searchResults[searchIndex];
            }
        },
        methods: {
            render() {
                const { hot } = this.get();
                hot.render();
            },
            doSearch() {
                const { hot, search } = this.get();
                clearTimeout(searchTimer);
                searchTimer = setTimeout(() => {
                    if (!hot || !search) {
                        this.set({ searchResults: [] });
                    } else {
                        const searchPlugin = hot.getPlugin('search');
                        const searchResults = searchPlugin.query(search);
                        this.set({ searchResults });
                    }
                }, 300);
            },
            update() {
                const { data, transpose, firstRowIsHeader, skipRows, hot } = this.get();

                if (!data) return;

                // get chart
                const { dw_chart } = this.store.get();

                // pass dataset through chart to apply changes and computed columns
                const ds = dw_chart
                    .dataset(
                        dw.datasource
                            .delimited({
                                csv: data,
                                transpose,
                                firstRowIsHeader,
                                skipRows
                            })
                            .parse()
                    )
                    .dataset();

                this.set({ columnOrder: ds.columnOrder() });

                // construct HoT data array
                const hotData = [[]];
                ds.eachColumn(c => hotData[0].push(c.title()));

                ds.eachRow(r => {
                    const row = [];
                    ds.eachColumn(col => row.push(col.raw(r)));
                    hotData.push(row);
                });

                // pass data to hot
                hot.loadData(hotData);

                const cellRenderer = getCellRenderer(this, dw_chart, ds, HOT, {});

                hot.updateSettings({
                    cells: (row, col) => {
                        const { readonly } = this.get();
                        return {
                            readOnly:
                                readonly ||
                                (ds.hasColumn(col) && ds.column(col).isComputed && row === 0),
                            renderer: cellRenderer
                        };
                    },
                    manualColumnMove: []
                });

                this.set({ ds });
                this.set({ has_changes: clone(chart.get('metadata.data.changes', [])).length > 0 });

                HOT.hooks.once('afterRender', () => this.initCustomEvents());
                HOT.hooks.once('afterRender', () => this.fire('afterRender'));
                hot.render();
            },
            dataChanged(cells) {
                const { hot } = this.get();
                let changed = false;
                cells.forEach(([row, col, oldValue, newValue]) => {
                    if (oldValue !== newValue) {
                        const chart = this.store.get().dw_chart;
                        const { transpose } = this.get();
                        const changes = clone(chart.get('metadata.data.changes', []));
                        row = hot.toPhysicalRow(row);
                        col = chart.dataset().columnOrder()[col];
                        if (transpose) {
                            // swap row and col
                            const tmp = row;
                            row = col;
                            col = tmp;
                        }
                        // store new change
                        changes.push({
                            column: col,
                            row,
                            value: newValue,
                            previous: oldValue,
                            time: new Date().getTime()
                        });
                        chart.set('metadata.data.changes', changes);
                        changed = true;
                    }
                });
                if (changed) {
                    setTimeout(() => {
                        this.update();
                        chart.save();
                    }, 100);
                }
            },
            columnMoved(srcColumns, tgtIndex) {
                const { hot } = this.get();
                if (!srcColumns.length) return;
                const { columnOrder } = this.get();
                const newOrder = columnOrder.slice(0);
                const after = columnOrder[tgtIndex];
                const elements = newOrder.splice(srcColumns[0], srcColumns.length);
                const insertAt =
                    after === undefined ? newOrder.length : after ? newOrder.indexOf(after) : 0;
                newOrder.splice(insertAt, 0, ...elements);
                this.store.get().dw_chart.set('metadata.data.column-order', newOrder.slice(0));
                this.set({ columnOrder: newOrder });
                // update selection
                HOT.hooks.once('afterRender', () => {
                    setTimeout(() => {
                        this.fire('resetSort');
                        hot.selectCell(
                            0,
                            insertAt,
                            hot.countRows() - 1,
                            insertAt + elements.length - 1
                        );
                    }, 10);
                });
                this.update();
            },
            updateHeight() {
                const h = document
                    .querySelector('.ht_master.handsontable .wtHolder .wtHider')
                    .getBoundingClientRect().height;
                this.refs.hot.style.height = Math.min(500, h + 10) + 'px';
            },
            checkRange(r, c, r2, c2) {
                // check if
                // 1. only a single column is selected, and
                // 2. the range starts at the first row, and
                // 3. the range extends through he last row
                const { hot } = this.get();
                const { ds } = this.get();

                if (c === c2 && r === 0 && r2 === hot.countRows() - 1) {
                    // const chart = this.store.get('dw_chart');
                    // this.set({activeColumn: chart.dataset().column(c)});
                    return;
                }
                if (c !== c2 && r === 0 && r2 === hot.countRows() - 1) {
                    const sel = [];
                    for (let i = Math.min(c, c2); i <= Math.max(c, c2); i++) {
                        sel.push(
                            +document.querySelector(
                                `#data-preview .htCore tbody tr:first-child td:nth-child(${i + 2})`
                            ).dataset.column
                        );
                    }
                    this.set({ multiSelection: sel.map(i => ds.column(i)), activeColumn: null });
                    return;
                }
                this.set({ activeColumn: null, multiSelection: false });
            },
            initCustomEvents() {
                // wait a bit to make sure HoT is rendered
                setTimeout(() => {
                    // catch click events on A,B,C,D... header row
                    this.refs.hot.querySelectorAll('.htCore thead th:first-child').forEach(th => {
                        th.removeEventListener('click', topLeftCornerClick);
                        th.addEventListener('click', topLeftCornerClick);
                    });
                    // const cellHeaderClickHandler = cellHeaderClick(app);
                    this.refs.hot.querySelectorAll('.htCore thead th+th').forEach(th => {
                        th.removeEventListener('click', cellHeaderClick);
                        th.addEventListener('click', cellHeaderClick);
                    });
                }, 500);
            },

            getColumnFormat(name) {
                const { dw_chart } = this.store.get();
                const colFormats = dw_chart.get('metadata.data.column-format', {});
                return colFormats[name] || { type: 'auto', ignore: false };
            }
        },

        oncreate() {
            app = this;
            HOT.hooks.once('afterRender', () => this.initCustomEvents());

            window.addEventListener('keyup', evt => {
                const { activeColumn, ds } = this.get();
                if (!activeColumn) return;

                if (
                    evt.target.tagName.toLowerCase() === 'input' ||
                    evt.target.tagName.toLowerCase() === 'textarea'
                )
                    return;

                if (evt.key === 'ArrowRight' || evt.key === 'ArrowLeft') {
                    evt.preventDefault();
                    evt.stopPropagation();
                    const currentIndex = ds.indexOf(activeColumn.name());
                    if (evt.key === 'ArrowRight') {
                        // select next column
                        this.set({ activeColumn: ds.column((currentIndex + 1) % ds.numColumns()) });
                    } else {
                        // select prev column
                        this.set({
                            activeColumn: ds.column(
                                (currentIndex - 1 + ds.numColumns()) % ds.numColumns()
                            )
                        });
                    }
                }
            });

            const { dw_chart } = this.store.get();
            const hot = new HOT(this.refs.hot, {
                data: [],
                rowHeaders: i => {
                    const ti = HOT.hooks.run(hot, 'modifyRow', i);
                    // const ti = hot.getPlugin('ColumnSorting').translateRow(i);
                    return ti + 1;
                },
                colHeaders: true,
                fixedRowsTop: 1,
                fixedColumnsLeft: this.get().fixedColumnsLeft,
                filters: true,
                // dropdownMenu: true,
                startRows: 13,
                startCols: 8,
                fillHandle: false,
                stretchH: 'all',
                height: 500,
                manualColumnMove: true,
                selectionMode: 'range',
                autoColumnSize: { useHeaders: true, syncLimit: 5 },

                // // sorting
                sortIndicator: true,
                columnSorting: true,
                sortFunction: function (sortOrder, columnMeta) {
                    if (columnMeta.col > -1) {
                        const column = dw_chart.dataset().column(columnMeta.col);
                        const colType = column.type();
                        return (a, b) => {
                            if (a[0] === 0) return -1;
                            // replace with values
                            a[1] = column.val(a[0] - 1);
                            b[1] = column.val(b[0] - 1);
                            if (colType === 'number') {
                                // sort NaNs at bottom
                                if (isNaN(a[1]))
                                    a[1] = !sortOrder
                                        ? Number.NEGATIVE_INFINITY
                                        : Number.POSITIVE_INFINITY;
                                if (isNaN(b[1]))
                                    b[1] = !sortOrder
                                        ? Number.NEGATIVE_INFINITY
                                        : Number.POSITIVE_INFINITY;
                            }
                            if (colType === 'date') {
                                if (typeof a[1] === 'string') a[1] = new Date(110, 0, 1);
                                if (typeof b[1] === 'string') b[1] = new Date(110, 0, 1);
                            }
                            return (
                                (sortOrder === 'desc' ? -1 : 1) *
                                (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)
                            );
                        };
                    }
                    return (a, b) => a[0] - b[0];
                },
                afterGetColHeader: (col, th) => {
                    const { activeColumn, ds } = this.get();
                    if (!ds || !ds.hasColumn(col)) return;
                    if (
                        (col === 0 || col) &&
                        activeColumn &&
                        ds.column(col).name() === activeColumn.name()
                    ) {
                        th.classList.add('selected');
                    }

                    if (col === 0 || col) {
                        if (this.getColumnFormat(ds.column(col).name()).ignore) {
                            th.classList.add('ignored');
                        } else {
                            th.classList.remove('ignored');
                        }
                    }
                },
                // // search
                search: 'search'
            });

            window.HT = hot;
            this.set({ hot });

            HOT.hooks.add('afterSetDataAtCell', a => this.dataChanged(a));
            HOT.hooks.add('afterColumnMove', (a, b) => this.columnMoved(a, b));
            HOT.hooks.add('afterRender', () => this.updateHeight());
            HOT.hooks.add('afterSelection', (r, c, r2, c2) => this.checkRange(r, c, r2, c2));
        },

        onstate({ changed, current, previous }) {
            const hot = current.hot;
            if (!hot) return;

            if (changed.data) {
                this.update();
            }
            if (changed.firstRowIsHeader && previous && previous.firstRowIsHeader !== undefined) {
                this.update();
            }
            if (changed.hot) {
                this.update();
            }
            if (changed.search && previous) {
                this.doSearch();
                this.set({ searchIndex: 0 });
            }

            if (changed.searchResults) {
                hot.render();
            }
            if (changed.currentResult && current.currentResult) {
                hot.render();
                const res = current.currentResult;
                hot.scrollViewportTo(res.row, res.col);
                setTimeout(() => {
                    // one more time
                    hot.scrollViewportTo(res.row, res.col);
                }, 100);
            }
            if (changed.activeColumn) {
                setTimeout(() => hot.render(), 10);
            }
            if (changed.fixedColumnsLeft) {
                hot.updateSettings({ fixedColumnsLeft: current.fixedColumnsLeft });
            }
            if (changed.sorting) {
                hot.getPlugin('columnSorting').sort(
                    chart.dataset().indexOf(current.sorting.sortBy),
                    current.sorting.sortDir ? 'asc' : 'desc'
                );
            }
        }
    };

    function cellHeaderClick(evt) {
        const th = this;
        // reset the HoT selection
        // find out which data column we're in
        const k = th.parentNode.children.length;
        let thIndex = -1;
        // (stupid HTMLCollection has no indexOf)
        for (let i = 0; i < k; i++) {
            if (th.parentNode.children.item(i) === th) {
                thIndex = i;
                break;
            }
        }
        // find column index
        const colIndex = +app.refs.hot.querySelector(
            `.htCore tbody tr:first-child td:nth-child(${thIndex + 1})`
        ).dataset.column;
        const { dw_chart } = app.store.get();
        const { activeColumn, hot } = app.get();
        if (dw_chart.dataset().hasColumn(colIndex)) {
            const newActive = dw_chart.dataset().column(+colIndex);
            // set active column (or unset if it's already set)
            if (activeColumn && activeColumn.name() === newActive.name()) {
                evt.target.parentNode.classList.remove('selected');
                app.set({ activeColumn: false });
                hot.deselectCell();
            } else {
                evt.target.parentNode.classList.add('selected');
                app.set({ activeColumn: newActive });
            }
        }
    }

    function topLeftCornerClick(evt) {
        evt.preventDefault();
        const { transpose } = app.get();
        app.set({ transpose: !transpose });
        app.update();
    }
</script>

<style lang="less">
    :global(#data-preview) {
        width: 770px;
        /*height: 400px;*/
        overflow: auto;

        tr td,
        tr th {
            font-family: 'Roboto Mono', monospace;
            font-size: 12px;
            max-width: 250px;
        }

        colgroup col {
            max-width: 500px !important;
        }

        tr td {
            padding: 3px 6px;

            &.textType {
                color: #000000;
            }

            &.dateType {
                color: #39a832;
                text-align: center;
            }

            &.numberType {
                color: #297ea8;
                text-align: right;
            }

            &.noData {
                text-align: center;
                color: #cccccc;
            }

            &.parsingError {
                background: #fee;
                color: #c00;
            }

            &.readOnly {
                // background: mix(#F1F0E3, white);
                // &.evenRow { background: #F1F0E3; }
                &.firstRow {
                    background: #eaeaea;
                    border-right-color: #aaa;
                    color: #777;
                }
            }

            &.active {
                background: mix(#18a1cd, white, 10%);
                &.firstRow {
                    background: mix(#18a1cd, #eaeaea, 10%);
                }
                &.readOnly {
                }
            }

            &.changed {
                position: relative;

                &:after {
                    content: ' ';
                    width: 0;
                    height: 0;
                    border-top: 7px solid orange;
                    border-right: 7px solid transparent;
                    display: block;
                    position: absolute;
                    left: 0;
                    top: 0;
                }
            }

            &.htSearchResult {
                /*background: #ffd;*/
                background: #fff8ef;
                color: #8d6833 !important;
            }
            &.htCurrentSearchResult {
                color: #583707 !important;
                background: #fcedd9;
                /*background: #fffe83;*/
            }
        }

        thead {
            th {
                cursor: pointer;
                background: #eee;
                border-right: 1px solid hsl(210, 0%, 70%);
                &:first-child {
                    .cornerHeader:after {
                        display: block;
                        content: '';
                        width: 12px;
                        height: 12px;
                        background-image: url(/static/css/chart-editor/transpose.png);
                        background-size: contain;
                        position: absolute;
                        top: ~'calc(50% - 6px)';
                        left: ~'calc(50% - 6px)';
                    }
                }
                &.selected {
                    background: #18a1cd;
                    color: white;
                    font-weight: bold;
                }
                &:hover {
                    background: #ddd;
                }
                &.selected:hover {
                    background: #18a1cd;
                }
            }
            tr th + th div:after {
                // content: " "url(/static/css/chart-editor/selection-off.png);
                position: absolute;
                top: 3px;
                right: 5px;
            }

            th + th:hover div:after {
                // content: " "url(/static/css/chart-editor/selection-hover.png);
            }

            th + th.selected div:after {
                opacity: 1;
                // content: " "url(/static/css/chart-editor/selection.png);
            }
        } // end thead

        td.ignored,
        th.ignored {
            color: #aaa !important;
            text-decoration: line-through;
            &.firstRow {
                background-color: #ddd;
                &.area {
                    background: #d4ddee;
                }
            }
            background-color: #f4f4f4;
            /*&.area { background: #dce5f7; }*/
        }

        th.ignored {
            background-color: #e2e2e2;
            color: #aaa;
            text-decoration: line-through;
        }

        tbody tr:first-child td {
            background: #f3f3f3;
            font-weight: bold;
            border-bottom: 1px solid #999;
        }
        .htCommentCell:after {
            border-left: 8px solid transparent;
            border-top: 8px solid orange;
        }
        // custom highlight color
        .handsontable td.area-1::before,
        .handsontable td.area-2::before,
        .handsontable td.area-3::before,
        .handsontable td.area-4::before,
        .handsontable td.area-5::before,
        .handsontable td.area-6::before,
        .handsontable td.area-7::before,
        .handsontable td.area::before {
            background: #18a1cd;
        }

        .handsontable .columnSorting {
            pointer-events: none;
        }

        .handsontable .columnSorting.ascending::after {
            font: normal normal normal 14px/1 FontAwesome;
            content: '\f160'; // sort-amount-asc
            top: 0px;
            right: -17px;
        }
        .handsontable .columnSorting.descending::after {
            font: normal normal normal 14px/1 FontAwesome;
            content: '\f161'; // sort-amount-desc
            top: 0px;
            right: -17px;
        }
        .handsontable .selected .columnSorting::after {
            color: white;
        }
    }
</style>
